#pragma once

#include "type.h"

#include "vector3.h"
#include "quaternion.h"
#include "matrix4.h"

#include "Renderable.h"

namespace RenderSystem
{
	class Node
	{
	public:

		typedef std::vector<Node*>	ChildNode;
		typedef ChildNode::iterator IteratorChildNode;
		typedef ChildNode::const_iterator ConstIteratorChildNode;

		/** Enumeration denoting the spaces which a transform can be relative to.
		*/
		enum TransformSpace
		{
			/// Transform is relative to the local space
			TS_LOCAL,
			/// Transform is relative to the space of the parent node
			TS_PARENT,
			/// Transform is relative to world space
			TS_WORLD
		};

		Node(void);
		virtual ~Node(void);

		virtual void GetInitialState( Common::Vector3* pTranslation = NULL, Common::Quaternion* pRotation = NULL, Common::Vector3* pScale = NULL );

		virtual const Common::Vector3&		GetWorldTranslation( void );
		virtual const Common::Quaternion&	GetWorldOrientation( void );
		virtual const Common::Vector3&		GetWorldScale( void );
		virtual const Common::Matrix4&		GetWorldTransformation( void );

		void SetInheritance( bool* bInheritTranslation = NULL, bool* bInheritRotation = NULL, bool* bInheritScale = NULL );
		void GetInheritance( bool* bInheritTranslation = NULL, bool* bInheritRotation = NULL, bool* bInheritScale = NULL );

		virtual void Translate(const Common::Vector3& d, TransformSpace relativeTo = TS_PARENT);

		virtual void Roll(const Common::Radian& angle, TransformSpace relativeTo = TS_LOCAL);
		virtual void Pitch(const Common::Radian& angle, TransformSpace relativeTo = TS_LOCAL);
		virtual void Yaw(const Common::Radian& angle, TransformSpace relativeTo = TS_LOCAL);

		virtual void Rotate(const Common::Vector3& axis, const Common::Radian& angle, TransformSpace relativeTo = TS_LOCAL);
		virtual void Rotate(const Common::Quaternion& q, TransformSpace relativeTo = TS_LOCAL);

		virtual void Scale( const Common::Vector3& vScale );

		virtual Common::Matrix3 GetLocalAxes(void) const;

		//manipulate to children
		virtual Node* CreateChild( const Common::Vector3& translate = Common::Vector3::ZERO, const Common::Quaternion& rotate = Common::Quaternion::IDENTITY );
		virtual void AddChild(Node* child);
		virtual uint32 GetChildrenCount(void) const;
		virtual Node* GetChild( uint32 index ) const;  
		virtual Node* RemoveChild( uint32 index );
		virtual Node* RemoveChild( Node* child );
		virtual void RemoveAllChildren(void);

		virtual void SetParent( Node* pChild );
		virtual Node* GetParent(void) const;

		//convert function
		virtual Common::Vector3 ConvertWorldToLocalPosition( const Common::Vector3& worldPos );
		virtual Common::Vector3 ConvertLocalToWorldPosition( const Common::Vector3& localPos );
		virtual Common::Quaternion ConvertWorldToLocalOrientation( const Common::Quaternion& worldOrientation );
		virtual Common::Quaternion ConvertLocalToWorldOrientation( const Common::Quaternion& localOrientation );

		//update transformation
		virtual void NeedUpdate(void);
		virtual void UpdateFromParent(void);

	protected:

		virtual Node* CreateChildImpl(void) = 0;

		virtual void _Update(bool updateChildren, bool bParentHasUpdated);

	protected:

		/// Pointer to parent node
		Node*				m_pParent;
		ChildNode			m_Children;

		//tansformation in parent space
		Common::Quaternion  m_qLocalOrientation;
		Common::Vector3		m_vLocalPosition;
		Common::Vector3		m_vLocalScale;

		//accumulating matrix from parents
		Common::Quaternion  m_qWorldOrientation;
		Common::Vector3		m_vWorldPosition;
		Common::Vector3		m_vWorldScale;
		Common::Matrix4		m_matWorldTransformation;

		//record the initialized state, in world space
		Common::Vector3		m_vInitialPosition;
		Common::Quaternion	m_qInitialOrientation;
		Common::Vector3		m_vInitialScale;

		//transformation has been modified and need updating when we use
		bool				m_bUpdateParent;
		bool				m_bUpdateChildren;
		bool				m_bUpdateTransformationMatrix;

		//transformation inheritance
		bool				m_bInheritTranslation;
		bool				m_bInheritRotation;
		bool				m_bInheritScale;
	};
}